﻿using System;
using System.Threading;
using PoolKit.Strategies.Creation;

namespace PoolKit.Commands
{
    /// <summary>
    ///
    /// </summary>
    /// <typeparam name="TResource"></typeparam>
    internal class AddItemsUntilCountReachedCommand<TResource> : Command
    {
        private readonly IResourceCollection<TResource> _resourceCollection;
        private readonly int _intendedResourceCount;
        private readonly CancellationTokenSource _cancellationToken;
        private readonly ResourceCreationStrategy<TResource> _resourceCreationStrategy;
        private readonly Semaphore _coordinationSemaphore;

        public AddItemsUntilCountReachedCommand(
            IResourceCollection<TResource> resourceCollection,
            int intendedResourceCount,
            CancellationTokenSource cancellationToken,
            ResourceCreationStrategy<TResource> resourceCreationStrategy,
            Semaphore coordinationSemaphore)
        {
            if (resourceCollection == null) throw new ArgumentNullException("resourceCollection");
            if (cancellationToken == null) throw new ArgumentNullException("cancellationToken");
            if (resourceCreationStrategy == null) throw new ArgumentNullException("resourceCreationStrategy");
            if (coordinationSemaphore == null) throw new ArgumentNullException("coordinationSemaphore");

            if (intendedResourceCount <= 0)
            {
                throw new ArgumentOutOfRangeException("intendedResourceCount", Resources.ExpectingNumberGreaterThanZero);
            }

            if (resourceCreationStrategy.ThreadingContract != ThreadingContract.Multithreaded)
            {
                throw new ArgumentException("Expecting a thread-safe strategy", "resourceCreationStrategy");
            }

            _resourceCollection = resourceCollection;
            _intendedResourceCount = intendedResourceCount;
            _cancellationToken = cancellationToken;

            _resourceCreationStrategy = resourceCreationStrategy;
            _coordinationSemaphore = coordinationSemaphore;
        }

        /// <summary>
        /// </summary>
        /// <exception cref="CannotAddResourceException"></exception>
        public override void Execute()
        {
            try
            {
                while ((_resourceCollection.Count < _intendedResourceCount) && (!_cancellationToken.IsCancellationRequested))
                {
                    try
                    {
                        _resourceCollection.Add(_resourceCreationStrategy.Create());
                    }
                    catch (ArgumentNullException)
                    {
                        throw new CannotAddResourceException(Resources.CannotAddNullResource);
                    }
                    catch (CannotAddResourceException)
                    {
                        throw new CannotAddResourceException(Resources.CannotAddDuplicateResource);
                    }
                }
            }
            finally
            {
                // Note: releasing, so other similar commands can start.
                _coordinationSemaphore.Release();
            }
        }
    }
}